package main

import (
	"errors"
	"fmt"
	"math"
	"strconv"
)

// 自定义错误类型
type MyError struct {
	Code    int
	Message string
}

// 实现 error 接口
func (e *MyError) Error() string {
	return fmt.Sprintf("错误代码: %d, 错误信息: %s", e.Code, e.Message)
}

// 另一个自定义错误类型
type DivideError struct {
	Dividend float64
	Divisor  float64
}

func (e *DivideError) Error() string {
	return fmt.Sprintf("无法执行除法运算: %f / %f (除数不能为零)", e.Dividend, e.Divisor)
}

// 可能返回错误的函数
func divide(a, b float64) (float64, error) {
	if b == 0 {
		// 使用 errors.New 创建简单错误
		return 0, errors.New("除数不能为零")
	}
	return a / b, nil
}

// 返回自定义错误的函数
func sqrt(x float64) (float64, error) {
	if x < 0 {
		// 使用 fmt.Errorf 创建格式化错误
		return 0, fmt.Errorf("无法计算负数 %f 的平方根", x)
	}
	return math.Sqrt(x), nil
}

// 返回自定义错误类型的函数
func processData(data int) error {
	if data < 0 {
		return &MyError{
			Code:    1001,
			Message: "数据不能为负数",
		}
	}
	if data > 100 {
		return &MyError{
			Code:    1002,
			Message: "数据不能大于100",
		}
	}
	return nil
}

// 模拟文件操作可能出错的函数
func openFile(filename string) error {
	if filename == "" {
		return errors.New("文件名不能为空")
	}
	if filename == "protected.txt" {
		return &MyError{
			Code:    2001,
			Message: "没有权限访问文件",
		}
	}
	return nil
}

func main() {
	fmt.Println("14. 错误处理")

	// 基本错误处理
	fmt.Println("\n--- 基本错误处理 ---")

	// 处理可能返回错误的函数
	if result, err := divide(10, 2); err != nil {
		fmt.Printf("除法运算错误: %v\n", err)
	} else {
		fmt.Printf("10 / 2 = %f\n", result)
	}

	if result, err := divide(10, 0); err != nil {
		fmt.Printf("除法运算错误: %v\n", err)
	} else {
		fmt.Printf("10 / 0 = %f\n", result)
	}

	// 处理自定义格式化错误
	if result, err := sqrt(16); err != nil {
		fmt.Printf("开方运算错误: %v\n", err)
	} else {
		fmt.Printf("sqrt(16) = %f\n", result)
	}

	if result, err := sqrt(-4); err != nil {
		fmt.Printf("开方运算错误: %v\n", err)
	} else {
		fmt.Printf("sqrt(-4) = %f\n", result)
	}

	// 处理自定义错误类型
	fmt.Println("\n--- 处理自定义错误类型 ---")

	if err := processData(50); err != nil {
		fmt.Printf("处理数据错误: %v\n", err)
	} else {
		fmt.Println("数据处理成功")
	}

	if err := processData(-10); err != nil {
		fmt.Printf("处理数据错误: %v\n", err)

		// 类型断言检查具体错误类型
		if myErr, ok := err.(*MyError); ok {
			fmt.Printf("错误代码: %d\n", myErr.Code)
			fmt.Printf("错误信息: %s\n", myErr.Message)
		}
	}

	if err := processData(150); err != nil {
		fmt.Printf("处理数据错误: %v\n", err)

		// 类型断言检查具体错误类型
		if myErr, ok := err.(*MyError); ok {
			fmt.Printf("错误代码: %d\n", myErr.Code)
			fmt.Printf("错误信息: %s\n", myErr.Message)
		}
	}

	// 错误处理的实际应用
	fmt.Println("\n--- 错误处理的实际应用 ---")

	files := []string{"", "protected.txt", "normal.txt"}
	for _, file := range files {
		fmt.Printf("尝试打开文件: '%s'\n", file)
		if err := openFile(file); err != nil {
			fmt.Printf("打开文件失败: %v\n", err)

			// 根据错误类型采取不同处理方式
			switch e := err.(type) {
			case *MyError:
				fmt.Printf("自定义错误 - 代码: %d, 信息: %s\n", e.Code, e.Message)
				if e.Code == 2001 {
					fmt.Println("提示: 请检查文件权限")
				}
			default:
				fmt.Println("其他类型错误")
			}
		} else {
			fmt.Println("文件打开成功")
		}
		fmt.Println()
	}

	// 错误链和包装 (Go 1.13+)
	fmt.Println("\n--- 错误链和包装 ---")

	// 模拟多层函数调用中的错误处理
	if err := processUserInput("abc"); err != nil {
		fmt.Printf("处理用户输入错误: %v\n", err)

		// 检查错误是否包含特定错误
		var divideErr *DivideError
		if errors.As(err, &divideErr) {
			fmt.Printf("发现除法错误: %v\n", divideErr)
		}
	}

	// 错误处理最佳实践
	fmt.Println("\n--- 错误处理最佳实践 ---")

	// 1. 及早处理错误
	if err := validateAndProcess("123"); err != nil {
		// 记录错误并返回，不要继续执行
		fmt.Printf("验证处理失败: %v\n", err)
		return
	}

	// 2. 添加上下文信息
	if err := processWithContext("data.txt"); err != nil {
		// 使用 fmt.Errorf 添加上下文
		fmt.Printf("处理文件时出错: %v\n", err)
	}

	// 3. 不要忽略错误
	// 错误示例（不要这样做）:
	// result, _ := divide(10, 0) // 忽略错误

	// 正确示例:
	if result, err := divide(10, 0); err != nil {
		fmt.Printf("处理除法时出错: %v\n", err)
	} else {
		fmt.Printf("结果: %f\n", result)
	}
}

// 模拟多层函数调用
func processUserInput(input string) error {
	number, err := strconv.Atoi(input)
	if err != nil {
		return fmt.Errorf("转换输入为数字失败: %w", err)
	}

	result, err := safeDivide(float64(number), 0)
	if err != nil {
		return fmt.Errorf("执行除法运算失败: %w", err)
	}

	fmt.Printf("结果: %f\n", result)
	return nil
}

// 安全除法函数，返回自定义错误类型
func safeDivide(dividend, divisor float64) (float64, error) {
	if divisor == 0 {
		return 0, &DivideError{
			Dividend: dividend,
			Divisor:  divisor,
		}
	}
	return dividend / divisor, nil
}

// 验证并处理数据
func validateAndProcess(input string) error {
	if input == "" {
		return errors.New("输入不能为空")
	}

	if len(input) > 10 {
		return errors.New("输入长度不能超过10个字符")
	}

	fmt.Printf("处理输入: %s\n", input)
	return nil
}

// 带上下文的处理函数
func processWithContext(filename string) error {
	data, err := readFile(filename)
	if err != nil {
		return fmt.Errorf("读取文件 %s 失败: %w", filename, err)
	}

	if err := processDataString(data); err != nil {
		return fmt.Errorf("处理文件 %s 的数据失败: %w", filename, err)
	}

	return nil
}

// 模拟读取文件
func readFile(filename string) (string, error) {
	if filename == "notfound.txt" {
		return "", errors.New("文件不存在")
	}
	return "file data", nil
}

// 模拟处理字符串数据
func processDataString(data string) error {
	if data == "" {
		return errors.New("数据为空")
	}
	return nil
}
